#!/bin/bash

# -------------------------------------------------------------------------- #
# Copyright 2002-2023, OpenNebula Project, OpenNebula Systems                #
#                                                                            #
# Licensed under the Apache License, Version 2.0 (the "License"); you may    #
# not use this file except in compliance with the License. You may obtain    #
# a copy of the License at                                                   #
#                                                                            #
# http://www.apache.org/licenses/LICENSE-2.0                                 #
#                                                                            #
# Unless required by applicable law or agreed to in writing, software        #
# distributed under the License is distributed on an "AS IS" BASIS,          #
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   #
# See the License for the specific language governing permissions and        #
# limitations under the License.                                             #
#--------------------------------------------------------------------------- #

# clone fe:SOURCE host:remote_system_ds/disk.i vmid dsid [replica_host]
#   - fe is the front-end hostname
#   - SOURCE is the path of the disk image in the form DS_BASE_PATH/disk
#   - host is the target host to deploy the VM
#   - remote_system_ds is the path for the system datastore in the host
#   - vmid is the id of the VM
#   - dsid is the target datastore (0 is the system datastore
#   - replica_host is an optional paramter when called from the tm/ssh/clone

SRC=$1
DST=$2

VMID=$3
DSID=$4

REPLICA_HOST=$5

if [ -z "${ONE_LOCATION}" ]; then
    TMCOMMON=/var/lib/one/remotes/tm/tm_common.sh
    SSH_RC=/var/lib/one/remotes/etc/tm/ssh/sshrc
    SSH_UTILS=/var/lib/one/remotes/tm/ssh/ssh_utils.sh
else
    TMCOMMON=$ONE_LOCATION/var/remotes/tm/tm_common.sh
    SSH_RC=$ONE_LOCATION/var/remotes/etc/tm/ssh/sshrc
    SSH_UTILS=$ONE_LOCATION/var/remotes/tm/ssh/ssh_utils.sh
fi

. $TMCOMMON
. $SSH_RC
. $SSH_UTILS

DRIVER_PATH=$(dirname $0)
XPATH="${DRIVER_PATH}/../../datastore/xpath.rb --stdin"

#-------------------------------------------------------------------------------
# Set dst path and dir
#-------------------------------------------------------------------------------
SRC_PATH=$(arg_path $SRC)
DST_PATH=$(arg_path $DST)
SRC_FILE=$(basename $SRC_PATH)
DST_FILE=$(basename $DST_PATH)

SRC_HOST=$(arg_host $SRC)
DST_HOST=$(arg_host $DST)

SRC_DIR=$(dirname $SRC_PATH)
DST_DIR=$(dirname $DST_PATH)

#-------------------------------------------------------------------------------
# Get Image and System DS information
#-------------------------------------------------------------------------------
DISK_ID=$(basename ${DST_PATH} | cut -d. -f2)

unset i j XPATH_ELEMENTS

while IFS= read -r -d '' element; do
    XPATH_ELEMENTS[i++]="$element"
done < <(onevm show -x $VMID| $XPATH \
                    /VM/TEMPLATE/DISK[DISK_ID=$DISK_ID]/SIZE \
                    /VM/TEMPLATE/DISK[DISK_ID=$DISK_ID]/ORIGINAL_SIZE \
                    /VM/TEMPLATE/DISK[DISK_ID=$DISK_ID]/RECOVERY_SNAPSHOT_FREQ \
                    /VM/HISTORY_RECORDS/HISTORY[last\(\)]/DS_ID)

SIZE="${XPATH_ELEMENTS[j++]}"
ORIGINAL_SIZE="${XPATH_ELEMENTS[j++]}"
RECOVERY_SNAPSHOT_FREQ="${XPATH_ELEMENTS[j++]}"
SYS_DS_ID="${XPATH_ELEMENTS[j++]}"

# ------------------------------------------------------------------------------
# Get REPLICA_HOST from DATASTORE (if not passed as 5th arg)
# ------------------------------------------------------------------------------
if [ -z "$REPLICA_HOST" ]; then
    REPLICA_HOST=$(get_replica_host $SYS_DS_ID)

    if [ -z "$REPLICA_HOST" ]; then
        error_message "No REPLICA_HOST in datastore $SYS_DS_ID template"
        exit 1
    fi
fi

#-------------------------------------------------------------------------------
# Create DST path and enable local monitoring
#-------------------------------------------------------------------------------
ssh_make_path $DST_HOST $DST_DIR
enable_local_monitoring $DST_HOST $DST_DIR "ssh"

# ------------------------------------------------------------------------------
# Get REPLICA_STORAGE_IP from host template
# ------------------------------------------------------------------------------
REPLICA_STORAGE_IP=$(awk 'gsub(/[\0]/, x)' \
    <( onehost show $DST_HOST -x | $XPATH /HOST/TEMPLATE/REPLICA_STORAGE_IP ))

# ------------------------------------------------------------------------------
#  Synchronize Image Datastore in the Replica Host. Use a recovery snapshot
#  if present in the RECOVERY_SNAPS_DIR
#  (recovery snap existence means recreate is running, VMIDs is reused)
# ------------------------------------------------------------------------------
if recovery_snap_exists $REPLICA_HOST $VMID/$DST_FILE; then

    # point to [disk].recovery_snaphost files
    SRC_DIR=${REPLICA_RECOVERY_SNAPS_DIR}/$VMID
    SRC_FILE=$DST_FILE
    SRC_PATH="$SRC_DIR/$SRC_FILE"
    RECOVERY="YES"
else
    rsync_img_to_replica "$SRC_PATH" "$REPLICA_HOST"

    if [ -n "$ORIGINAL_SIZE" -a "$SIZE" -gt "$ORIGINAL_SIZE" ]; then
       RESIZE_CMD="qemu-img resize ${DST_PATH} ${SIZE}M"
    fi
fi

#-------------------------------------------------------------------------------
# Clone (tar|ssh) SRC into DST
#-------------------------------------------------------------------------------
log "Cloning $SRC_PATH via replica $REPLICA_HOST in $DST"

# copy locally, we hit the replica
if [ "$REPLICA_HOST" = "$DST_HOST" ]; then
    # if recovery snapshot is needed, prepare base <- base.1 qcow2 structure
    CLONE_CMD=$(cat <<EOF
        # this only prints create_base function content
        $(type create_base| grep -v 'is a function')

        if [ "$RECOVERY" != "YES" ] && [ -n "$RECOVERY_SNAPSHOT_FREQ" ]; then
            create_base "$SRC_PATH" "$DST_PATH"
        else
            cd $SRC_DIR

            cp -P $SRC_FILE $DST_PATH

            if [ -d $SRC_PATH.snap ]; then
                cp -r $SRC_PATH.snap $DST_PATH.snap
                ( cd $DST_PATH.snap; ln -s . $DST_FILE.snap )
            fi
        fi
EOF
)

else
    # copy to remote using faster tar|ssh
    CLONE_CMD=$(cat <<EOF
        if [ -d "${SRC_PATH}.snap" ]; then
            SRC_SNAP="${SRC_FILE}.snap"
        fi

        cd $SRC_DIR
        $TAR --transform="flags=r;s|$SRC_FILE|$DST_FILE|" -cSf - $SRC_FILE \$SRC_SNAP | \
            ssh $REPLICA_SSH_OPTS ${REPLICA_STORAGE_IP:-$DST_HOST} "$TAR xSf - -C $DST_DIR"
EOF
)

    # if recovery snapshot is needed, prepare base <- base.1 qcow2 structure
    if [ "$RECOVERY" != "YES" ] && [ -n "$RECOVERY_SNAPSHOT_FREQ" ]; then
        MAKE_SNAP_CMD=$(cat <<EOF
            # this only prints create_base function content
            $(type create_base| grep -v 'is a function')

            create_base "$DST_PATH" "$DST_PATH" "mv"
EOF
)
    else
        # when recovery running create only ". -> disk.X.snap" symlink
        MAKE_SNAP_CMD="cd $DST_PATH.snap; ln -s . $DST_FILE.snap"
        MAKE_SNAP_CMD=$(cat <<EOF
            if [ -d $DST_PATH.snap ]; then
                cd $DST_PATH.snap
                ln -s . $DST_FILE.snap ||:
            fi
EOF
)
    fi
fi

# Prepare base image copy on REPLICA for recovery snapshots
if [ -n "$RECOVERY_SNAPSHOT_FREQ" ] && [ "$RECOVERY" != "YES" ]; then
    SNAP_DIR="$REPLICA_RECOVERY_SNAPS_DIR/$VMID"

    # TODO: Consider multiple subsequent recoveries
    REPLICA_BASE_CMD=$(cat <<EOF
        set -e -o pipefail

        mkdir -p $SNAP_DIR/$DST_FILE.snap
        ln -f -s $DST_FILE.snap/base.1 $SNAP_DIR/$DST_FILE
        cp $SRC_PATH $SNAP_DIR/$DST_FILE.snap/base
EOF
)

    ssh_forward ssh_exec_and_log "$REPLICA_HOST" "$REPLICA_BASE_CMD" \
        "Error uploading base image to replica"
fi

ssh_forward ssh_exec_and_log $REPLICA_HOST "$CLONE_CMD" \
    "Error copying $SRC to $DST"

if [ -n "$MAKE_SNAP_CMD" ]; then
   ssh_exec_and_log "$DST_HOST" "$MAKE_SNAP_CMD" \
       "Error resizing image $DST"
fi

if [ -n "$RESIZE_CMD" ]; then
   ssh_exec_and_log "$DST_HOST" "$RESIZE_CMD" \
       "Error resizing image $DST"
fi
